home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
lisp207.arc
/
PC-LISP.DOC
< prev
next >
Wrap
Text File
|
1986-03-26
|
75KB
|
1,588 lines
A GUIDE TO THE PC-LISP INTERPRETER (V2.07)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By Peter Ashwood-Smith
~~~~~~~~~~~~~~~~~~~~~~
University of Toronto,
~~~~~~~~~~~~~~~~~~~~~
Ontario, Canada.
~~~~~~~~~~~~~~~
With thanks to Brian Robertson for the math functions.
Feb 01/86.
email: petera!utcsri or br!utcsri
mail: Peter Ashwood-Smith
#811, 120 St. Patrick St.
Toronto, Ontario,
Canada,
M5T-2X7.
phone: (416) 593-7574.
1
INTRODUCTION
~~~~~~~~~~~~
PC-LISP is a small implementation of LISP for any MS-DOS
machine. While small, it is capable of running a reasonable
subset of Franz LISP. It is not 100% compatible with Franz but
the functions have the same names and do the same things as Franz
when possible. This is version 2.07 and is the result of about
1.5 years of work. Version 2.07 has these features:
- types float, alpha, list, and port, function bodies
lambda, nlambda and macro.
- full garbage collection of all types.
- compacting, relocating heap space for all strings.
- shallow binding techniques for O(1) symbol value
lookup. (Dynamic scoping).
- access to some MSDOS BIOS graphics routines.
- sufficient built in functions to allow implementation
in LISP of most Franz or other functions. Includes
prog,eval,apply,defun and def.
- stack overflow detection & full error checking
on all calls, tracing of user defined functions,
and dumping of stack via (showstack).
- One level of break from which bindings at point
of error can be seen.
- Access to as much (non extended) memory as you've
got and control over how this memory is spread
among the various data types.
- Requires a bare minimum of 256K but the garbage
collection frequency is most bearable with 512K+.
This version is ShareWare. The idea is that you can make as
many copies as you like and distribute them to anyone or any BBS
you please, the more the better. My only requests are that you
not use it for profit of any kind, or remove the name(s) of
myself or any contributing authors from the program header or
from this manual. If you decide that you like the program then
send us 15$ to contribute to future development costs. Future
development will consist of adding more types including strings,
arrays and the functions that operate on them. If I have time and
there is enough interest I may also add a compiler.
The program is written 98% in C using the Lattice C 2.03
compiler with some extra assembly language routines to make up
for the otherwise excellent Lattice compiler and libraries. The
math library was written for me by Brian Robertson also of the
University of Toronto.
2
A WARNING
~~~~~~~~~
As I mentioned previously this program was compiled with the
Lattice C compiler, as such the program contains code to which
Lattice Inc. holds a copyright. If you sell it I can only get
angry but Lattice could take you to court. And, as with all
software you use it at your own risk. I will not be held
responsible for loss of any kind as a result of the correct or
incorrect use of this program.
A NOTE
~~~~~~
The rest of this manual assumes some knowledge of LISP,
MSDOS and a little programming experience. If you are new to LISP
or programming in general you should work your way through a book
on LISP such as LISPcraft by Robert Wilensky. You can use the
interpreter to run almost all of the examples in the earlier
chapters. I obviously cannot attempt to teach you LISP here
because it would require many hundreds of pages and there are
much better books on the subject than I could write. Also, there
are other good books on Franz LISP besides LISPcraft so I am not
endorsing it. It just happens to be the book that I use.
EXAMPLE FUNCTIONS INCLUDED IN EXTFUNC.L
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For those of you that are like me and hate to read a manual
you can take a look in the file EXTFUNC.L which should have
come with the interpreter. This file contains lots of function
definitions to help fill the gap between PC and Franz LISP. It
also contains a small Turtle Graphics demonstration. If you want
to start learning LISP or try out the demonstration you should
start PC-LISP by typing "PC-LISP EXTFUNC.L" from MS-DOS. Then
wait a bit until you see the "-->" prompt. Be patient if you have
512K or more memory because it takes a few seconds for PC-LISP to
organize your memory. If you only have a 256K machine, now is the
time to start thinking about an extra 256K. PC-LISP will be much
faster not having to garbage collect all the time. Anyway you now
have the prompt "-->" and EXTFUNC.L is loaded. To run the
graphics demo you just type "(GraphicsDemo)" without the quotes,
followed by ENTER. You must have a graphics adapter for this demo
to run. Don't even try it if your machine has no graphics
capability. (Graphics are slow but thats the price of portability
across MS-DOS machines). Note that if you want to start learning
LISP from a book such as LISPcraft, you should also load
"EXTFUNC.L" as functions that are referenced in the first couple
of chapters are defined in this file. The functions defined in
EXTFUNC.L are not very effecient so you may want to replace them
with your own definitions.
3
USERS GUIDE
~~~~~~~~~~~
The PC-LISP program is self contained. To run it just type
the command PC-LISP or whatever you called it. When it starts it
will start grabbing memory in chunks of 16K each. These will be
allocated to the three basic data types in percentages that you
can specify via 2 environment variables. The default is that 25%
of the memory will be allocated for alpha atoms. 15% will be
allocated for heap space, and the rest for cons,port, and float
cells. If you set the environment variables LISP%HEAP and
LISP%ALPH to an integer between 1 and 90 these will become the
new percentages for the heap and alpha respectively, the rest
going to cons, port, and float cells. Note that the percentages
are only accurate to the nearest 16K boundary. In other words the
set of 16K blocks are divided among the three types as closely to
the percentages that you specify as possible. PC-LISP will then
print the banner message, the total memory available and the
percentages that are allocated to each object. Next it will begin
to read the parameters from the command line. The command usage
is:
*
PC-LISP [=n..n] [ [+s][-s] file ]
The optional parameter =nnnn is the Lattice set stack size
option. It is preset to 32K and cannot be set smaller. You may
set it larger up to 64K if you wish. A 32K stack gives you about
466 recursive calls, 50K = 731 calls, 60K = 878 calls, and 64K =
936 calls. The Lattice C compiler does not generate code that
will handle stacks larger than 64K.
The +s and -s options turn on and off the printing of
statistical information while the file 'file' is being read and
evaluated. Statistical information is just a line telling what
percentage of cell, alpha and heap space you have used. Garbage
collection occurs at 100% use for a given object. The statistical
info is useful to see just how efficient/inefficient your
functions are. You can get the same information in list form by
calling the PC-LISP function (memstat) where the three numbers
returned correspond to the percentage of cell, alpha and heap
space used so far.
The files on the command line are processed one by one. This
consists of reading each list in the file and evaluating it. The
results are not printed on the console. Finally when all the
files have been processed you will find yourself with the LISP
top level prompt '-->'. Typing control-Z and ENTER (MS-DOS end of
file) when you see the '-->' prompt will cause PC-LISP to exit to
whatever program called it. If an error occurs you will see the
prompt 'er>'. For more info see the 'TERMINATION OF EVALUATION'
section of this manual and the commands (showstack), (trace), and
(untrace).
4
SYNTAX
~~~~~~
You will now be in the LISP interpreter and can start to
play with it. Basically it is expecting you to type an S-
expression. Where an S-expression is an atom, or a list and:
An atom may be one of three kinds. It may be an alpha atom ,
a real number atom, or a literal alpha atom. An alpha atom is
just a letter followed by letters/digits and certain special
symbols. There may be no more than 254 characters in the alpha
atom. To allow you to enter any text as an atom you may delimit
the atom with |'s. These will define a literal alpha atom in
which you may place any character between the delimiters (except
| itself). A real atom is just as you might think an optional
plus or minus sign followed by a sequence of digits, followed
optionally by a radix point and more digits. Sorry, exponential
notation is not supported. It should get into the next version.
A list is just a left ( followed by a of sequence of atoms
or lists followed by a right ). A list may also be a sequence of
atoms or lists followed by a '.' followed by an atom followed by
a right parenthisis ). This is called a dotted pair and it means
that the CAR and CDR of a list are both atoms. Note that a space
on either side of the dot is essential syntactically. You may
optionally place [ and ] in the list to represent meta-
parenthesis. Basically the ] just closes all open lists up to the
nearest [, or to the beginning of the list if no [ is present.
Here are some example legal lists.
(now is (the . time)) ; dotted pair (the . time)
(1 now16 (is (the (time ] ; the ] closes all 4 ('s
(car [quote(a b c d]) ; the ] closes 2 ('s to ]
(ThisIsBiggerAtom012345678) ; Upper case is ok
() ; empty list is equiv to 'nil
(1 (-2.2 +3.333)) ; some numbers all floats!
((((((|all one atom|] ; "all one atom" spaces too!
Note that you are allowed to mix any number of spaces, line-
feeds, carriage returns, form feeds, tabs etc. into your input.
Comments may start at any point in a line and will continue until
the end of the line as shown in the above example lists.
READ MACRO QUOTE
~~~~~~~~~~~~~~~~
PC-LISP supplies one read macro called 'quote' and written
using the little ' symbol. (User read macros in later versions)
This read macro is just a short hand way of writing the list
(quote XX). Where XX is what follows the '. Here are some
examples of what the read macro will do to your input before
passing it to the evaluator.
'apples -- goes to --> (quote apples)
'|too late| (quote |too late|)
'(1 2 3) (quote (1 2 3))
5
SYNTAX ERRORS
~~~~~~~~~~~~~
When you enter a list which is not correctly nested the
interpreter will return the wonderfully informative 'syntax
error' message. You will have to figure out where it is in the
input list. Note that if you do not finish entering a list, ie
you put one too few closing )'s on the end, the interpreter will
wait until you enter it before continuing. If you are not sure
what has happened just type "]]" and all lists will be closed and
the interpreter will try to do something with the list. If you
are running input from a file the interpreter will detect the end
of file and give you a 'syntax error' because the list was
unclosed.
EVALUATING S-EXPRESSIONS
~~~~~~~~~~~~~~~~~~~~~~~~
An S-expression may be an atom or a list. If it is an atom
the evaluation of it is its current binding. Most atoms are not
bound to begin with so just entering an atom will result in the
error 'unbound atom'. Float atoms are bound to their actual
values so when you enter 2 you will get 2 back again. Evaluating
a list consists of calling the function named or given by the
first element in the list with parameters given by the rest of
the list. For example there is a function called '+' which takes
any number of float values and returns their sum. So:
-->(+ 2 4 6 8)
Would return the result of 2+4+6+8 ie 20. We can also
compose these function calls by using list nesting. For example
we can subtract 2+4 from 6+8 as follows:
-->(- (+ 6 8) (+ 2 4))
We can also perform operations on other types of atoms.
Suppose that we wanted to reverse the list (now is the time).
There is a built in function called 'reverse' that does just what
we want. So we could try typing.
-->(reverse (now is the time))
But the interpreter will be confused! It does not know that
'now' is data and not a function taking arguments 'is', 'the'
and 'time'. We must use the function 'quote' which returns its
arguments unevaluated, hence its name "quote".
-->(reverse (quote (now is the time)))
Will give us the desired result (time the is now). But we
can do the same thing without using the (quote) function
directly. Remember the read macro ' above? Well it will replace
the entry '(now is the time) with (quote (now is the time)).
Hence we type:
-->(reverse '(now is the time))
6
EVALUATING S-EXPRESSIONS CONT'D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This gives us the correct result without as much typing. You
will note that the subtraction of 2+4 from 6+8 could also have
been entered as:
-->(- (+ '6 '8) (+ '2 '4))
However, the extra 's are redundant because a float
evaluates to itself. In general a LISP expression is evaluated by
first evaluating each of its arguments, and then applying the
function to the arguments, where the function is the first thing
in the list. Remember that evaluation of the function (quote s1)
returns s1 unevaluated. LISP will also allow the function name
to be replaced by a function body called a lambda expression. So
a legal input to the interpreter could be:
-->((lambda(x)(+ x 10)) 14)
Which would be processed as follows. First the parameters to
the lambda expression are evaluated. That just 14. Next the body
of the lambda expression is evaluated but with the value 14 bound
to the formal parameter given in the lambda expression. So the
body evaluated is (+ x 10) where x is bound to 14. The result is
just 24. Note that lambda expressions can be passed as parameters
as can built in functions or user defined functions. So I can
evaluate the following expression.
-->((lambda(f x)(f (car x))) '(lambda(l)(car l)) '((hi)))
Which evaluates as follows. The parameters to the call which
are the expressions '(lambda(l)(cdr l)) and '((hi)) are
evaluated. This results in the expressions being returned because
they are quoted. These are then bound to 'f and 'x respectively
and the body of the first lambda expression is evaluated. This
means that the expression ((lambda(l)(car l))(car ((hi)))) is
evaluated. So again the parameters to the function are evaluated.
Since the only parameter is (car ((hi))) it is evaluated
resulting in (hi). This is then bound to l and (car l) is
evaluated giving "hi".
PC-LISP is also capable of handling lambda expressions with
multiple bodies, nlambda expressions with multiple bodies and
labeled lambda and nlambda expressions. See the Built In
Functions Section which follows for more details on lambda and
nlambda. A slightly restricted macro form is also permitted. For
information on macros see the MACRO section of the manual.
7
TERMINATION OF EXPRESSION EVALUATION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are three distinct ways that evaluation can terminate.
First, evaluation can end naturally when there is no more work to
do. In this case the resulting S-expression is printed on the
console and you are presented with the prompt "-->". Second, you
can request premature termination by hitting the CONTROL-BREAK or
CONTROL-C keys simultaneously (hereafter referred to as CONTROL-
BREAK). Note that this will only interrupt list evaluation, it
will not interrupt garbage collection which continues to
completion. So, if you hit CONTROL-BREAK or CONTROL-C and you
don't get any response, wait a second or two because it will
respond after garbage collection ends. Finally, execution can
terminate when PC-LISP detects a bad parameter to a built in
function, a stack overflows, a division by zero is attempted, or
an atom is unbound etc. In all cases but a normal termination you
will be returned to a break error level. This is when the prompt
looks like 'er>'. This means that variable bindings are being
held for you to examine. So if the evaluation aborts with the
message "error in built in function [car]", you can examine the
atom bindings that were in effect when this error occurred by
typing the name of the atom desired. This causes its binding to
be displayed. When you are finished with the break level just hit
CONTROL-Z plus ENTER and you will be placed back in the normal
top level and all bindings that were non global will be gone.
Note you can do anything at the break level that you can do at
the top level. If further errors occur you will stay in the break
level and any bindings at the time of the second error will be in
effect as well as any bindings that were in effect at the
previous break level. If bindings effecting atoms whose values
are being held in the first break level are rebound at the second
break level these first bindings will be hidden by the secondary
bindings.
It is worth noting that when an error occurs in 'eval' or
'apply' it usually means that a parameter is being passed
incorrectly to a user defined function. Since the error message
will refer to a built in function when it tells you about an
error you will have to figure out which one of your functions
made that call.
It is also useful to know what the circumstances of the
failure were. You can display the last 20 evaluations with the
command (showstack). This will print the stack from the top to
the 20th element of the stack. This gives you the path of
evaluation that lead to the error. For more information on the
(showstack) command look in the section FUNCTIONS WITH SIDE
EFFECTS OR THAT ARE EFFECTED BY SYSTEM.
8
THE BUILT IN FUNCTIONS AND VARIABLES
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Following is a list of each built in function. I will denote
the allowed arguments as follows:
- a1...aN are alpha atom parameters.
- f1...fN are float atom parameters.
- l1...lN are list expression parameters.
- p1...pN are port atom parameters.
- s1...sN are S-expressions (any atom type or list)
- {a|d}+ means any sequence of characters of length greater
than 0 consisting of a's and d's in any combination. This
defines the car,cdr,cadr,caar,cadar... function class as
follows: "c{a|d}+r".
- [a1], [f1], [l1], [p1], [s1], etc... are optional parameters
of the indicated type. A default will be provided if they
are not present in the parameter list for the indicated
function.
- *a1*, *f1*, *l1*, *p1* or *s1* this means that the arguments
of the function are NOT evaluated prior to the function call
hence it is not necessary to quote them.
For the simpler functions I will describe the functions
using a sort of "if (condition) result1 else result2" notation
which should be pretty obvious to most people. For functions that
are a little more complex I will give a short English description
and perhaps an example. If the example code shows the '-->'
prompt you should be able to type exactly what follows each
prompt and get the same responses from PC-LISP. If the example
does not show a '-->' prompt the example is a code fragment which
requires additional code to function.
The functions are placed into 7 categories. These are as
follows: The math functions, the boolean functions, list and atom
creators and selectors, file I/O functions, functions with side
effects or that are effected by state of system, list evaluation
control functions and the MSDOS bios calls for graphics output.
Note that the BIOS calls are still experimental. Specifically
the line drawing algorithm does not draw lines as smooth or as
fast as I would like. This should be fixed by the next release.
9
THE MATH FUNCTIONS
~~~~~~~~~~~~~~~~~~
The functions manipulate floats. They are mostly obvious.
Note that these function work on floats while the Franz functions
work on -numbers-. Anywhere in PC-LISP where the parameter is
giving position or ordinal information the nearest integer to
the float parameter is taken as the correct value.
(abs f1) ----> absolute value of f1 is returned.
(acos f1) ----> arc cosine of f1 is returned.
(asin f1) ----> arc sine of f1 is returned.
(atan f1 f2) ----> arc tangent of ( f1 divided by f2 ).
(cos f1) ----> cosine of f1, f1 is radians
(exp f1) ----> returns e to the power f1.
(expt f1 f2) ----> returns f1**f2 using: exp(f2*log(f1))
(fact f1) ----> returns factorial of nearest int to f1.
(log f1) ----> natural logarithm of f1.
(log10 f1) ----> base 10 logarithm of f1.
(max f1..fn) ----> largest of f1...fn.
(min f1..fn) ----> smallest of f1...fn.
(mod f1 f2) ----> r1 modulo r2.
(random [f1])----> random float in 0..32K or 0..f1
(sin f1) ----> sine of f1, f1 is radians.
(sqrt f1) ----> square root of f1.
(* f1 .. fn) ----> f1*f2*f3*.....fn (or 1 if n = 0)
(+ f1 .. fn) ----> f1+f2+f3+.....fn (or 0 if n = 0)
(- f1 .. fn) ----> f1-f2-f3-.....fn (or 0 if n = 0)
(/ f1 .. fn) ----> f1/f2/f3/.....fn (or 1 if n = 0)
(< f1 f2) ----> if (f1 < f2) t else nil;
(= f1 f2) ----> if (f1 = f2) t else nil;
(> f1 f2) ----> if (f1 > f2) t else nil;
One thing worth noting is that when you compare two floats
you must use = or equal. You cannot use eq because the two float
values will almost certainly be stored in different cells even if
they have the same value. Hence eq will return nil because they
are not the same object. Note that Franz will return 't when you
compare two fixnums using eq because car and cdr pointers are
used to store the actual fixnum itself hence they appear to be
the same object. This is an interpreter dependent trick and PC-
LISP does not do it.
Consider this example which computes the sum of the cubes of
a list of floats called X which is setq'ed to (1 2 3 4 5):
-->(setq X '(1 2 3 4 5))
(1 2 3 4 5)
-->(eval (cons '+ (mapcar '* X X X)))
225
It operates as follows: First mapcar computes the products
1*1*1, 2*2*2,... 5*5*5 and creates the list (1 8 27 64 125). Then
cons splices a '+ onto the front creating (+ 1 8 27 64 125).
Finally eval gets the list and computes the result 225.
10
THE BOOLEAN FUNCTIONS
~~~~~~~~~~~~~~~~~~~~~
These functions all return boolean values. The values 't and
'nil represent true and false respectively. 't and 'nil are
predefined atoms whose bindings are exactly themselves. Note that
'nil is equivalent to the empty list (). It is the only object
that is both at atom and a list.
(alphalessp a1 a2) ---> if (ASCII a1 < a2) t else nil;
(atom s1) ---> if (s1 of type atom) t else nil;
(and s1 s2 .. sN) ---> if (a1...aN all != nil) t else nil;
(boundp a1) ---> if (a1 bound) (a1.eval(a1)) else nil;
(eq s1 s2) ---> if (s1 & s2 same object) t else nil;
(equal s1 s2) ---> if (s1 looks like s2) t else nil;
(floatp s1) ---> if (s1 of type float) t else nil;
(listp s1) ---> if (s1 of type list) t else nil;
(not s1) ---> if (s1 != nil) nil else t;
(null s1) ---> if (s1 != nil) nil else t;
(or s1 s2 .. sN) ---> if (any si != nil) t else nil;
(portp s1) ---> if (s1 of type port) t else nil;
Where ASCII a1 < a2 means that string a1 comes before string
a2 using the ASCII codes for the characters in both a1 and a2.
And, a1 bound means that a1 has been assigned a value. And, s1
looks like s2 means that s1 and s2 have the same list structure
and the leaves point to the same atoms. And, s1 and s2 the same
object means that they are exactly the same list or the same
atom.
Note that (eq 'a 'a) will always return 't because the
object 'a is unique to the LISP interpreter. On the other hand
the call (eq '(a) '(a)) will always return 'nil because the two
parameters '(a) and '(a) are distinct lists created by the read
function. However (equal '(a) '(a)) will return 't because the
two lists are of the same form and have the same leaves.
11
LIST & ATOM CREATORS AND SELECTORS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These functions will take lists and atoms as parameters and
return larger or smaller lists or atoms. They have no side
effects on the LISP system nor are their results affected by
anything other than the values of the parameters given to them.
(append l1..ln) ---> list made by joining all of l1..ln.
(ascii f1) ---> atom with name 'char' where 'char'
has ordinal value f1:(0 < f1 < 255).
(assoc a1 s2) ---> if s2 is a list of (atom.value) pairs
then assoc --> (a1.value) from s2.
(car l1) ---> first element in l1
(cdr l1) ---> l1 without first element of l1.
(c{a|d}+r l1) ---> performs repeated car or cdr's on
l1 as given by reverse of {a|d}+.
(cons s1 s2) ---> list with s1 as 1st elem s2 is rest.
(explode a1) ---> list of chars in name of a1.
(exploden a1) ---> list of ascii values of chars in a1.
(implode l1) ---> atom with name formed by compressing
all atom elements of l1.
(length l1) ---> float atom = # of elements of l1
(list s1 s2...sN) ---> a list with elements (s1 s2 ...sN)
(nth f1 l1) ---> f1'th element of l1 (indexed from 0)
(nthchar a1 f1) ---> f1'th char in name of a1 (from 0)
(pairlis l1 l2 l3) ---> l1 is list of atoms. l2 is a list
of S-expressions. l3 is a list of
((a1.s1)....) The result is the
pairing of atoms in l1 with values
in l2 with l3 appended (see assoc).
(quote *s1*) ---> exactly s1 unevaled without changes.
(reverse l1) ---> the list l1, reversed at top level.
(type s1) ---> list,float,port,alpha or other as
determined by type of parameter s1.
12
FILE I/O FUNCTIONS
~~~~~~~~~~~~~~~~~~
These functions perform simple list/atom and character I/O
you must be careful when writing lists to files to terminate with
a new line before closing the file. Otherwise they may cause
problems for some MS-DOS editors etc. These functions operate on
type 'port' which is returned by 'fileopen' and which when
printed is just %file% where 'file' is the name of associated
port.
(close p1) ---> closes the port p1 and returns t.
(fileopen a1 a2) ---> port to read write to/from to
file a1 using mode a2. Where
modes are 'r 'w 'rw 'rwa ...
(load a1) ---> The file a1 is read and all lists
are evaluated. LISP will look
first for file a1, then a1.l
then for LIB/a1 and finally for
LIB/a1.l. Where LIB is the value
of the environment variable :
LISP%LIB
(patom a1 [p1]) ---> print atom without delimiters
to port p1. Default to standard
output if p1 not present. returns
a1.
(print s1 [p1]) ---> print list with delimiters on
atoms to p1 or standard out.
(read [p1 [s1]]) ---> returns S-expression read from
p1 or standard input and returns
it or nil on end of file. If s1
is present it returns s1 on EOF.
(readc [p1 [s1]]) ---> returns character read from p1
or standard input and returns it
or nil on end of file. If s1 is
present it returns s1 on EOF.
A word about delimiters. Unless you use the patom function
all printing of atoms will be done in such a way that they can be
read again and produce the same structure. This means that if the
atom was originally entered with the | delimiters it will be
printed with them. Or, if you created an atom via an implode or
ascii function call that has a funny value in it, it will print
with the delimiters. So if you want to print a prompt on the
screen with spaces in it the best way is to enter (patom '|my
prompt|). Note also that 'r 'w and 'a stand for read, write and
append respectively.
13
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These functions will either have an effect on the way the
system behaves in the future or will give you a result about the
way the system has behaved in the past and future calls will not
necessarily give the same results.
(def *a1* *l1*)
~~~~~~~~~~~~~~~
a1 is a function name and l1 is a lambda, nlambda or macro
body. The body is associated with the atom a1 from now on and can
be used as a user defined function. Def returns a1.
-->(def first (lambda(x)(car x)))
-->(def second (lambda(x)(first(cdr x))))
-->(def sideff (lambda(x)(print x)(caddar x))))
-->(def plus (nlambda(l)(eval(cons '+ l))))
-->(def firstm (macro(l)(cons 'car (cdr l))))
(defun *a1* [*a2*] *l1* *s1* *s2* ....*sN*)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Defun will do the same job as "def" except that it will
build the lambda or nlambda expression for you. a1 is the name of
the function. a2 if present must be one of expr or fexpr. If it
is not present it defaults to expr. l1 is the list of formal
parameters which for an fexpr (nlambda) should contain one atom
formal parameter name. s1...sn are bodies for the lambda or
nlambda expression. The following example produces the same
effects as the above "def" calls. Defun returns the atom name of
the function that it defines. See MACROS for (defun x macro...)
-->(defun first(x)(car x))
-->(defun second(x)(first(cdr x)))
-->(defun sideff(x)(print x)(caddar x)))
-->(defun plus fexpr(l)(eval(cons '+ l)))
-->(defun firstm macro(l)(cons 'car (cdr l)))
(exit)
~~~~~~
The LISP interpreter will exit to MSDOS. Depending on how
much memmory you have it may ask for a system disk to reload
COMMAND.COM. Note that the video mode will not be reset if you
call (exit). It will however be reset to 80x25B&W if you quit
with a control-Z from the top level.
(gc)
~~~~
Starts garbage collection of alpha and cell space. Returns t
(gensym [a1])
~~~~~~~~~~~~~
Will generate a new alph atom whose name is gnnnnn where
nnnnn will be different each time. It is guaranteed to generate a
new atom. If a1 is present then the atoms have the form (name of
a1)nnnnn.
14
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CONT'D
~~~~~~
(get a1 a2)
~~~~~~~~~~~
Will return the value associated with property key a2 in
a1's property list. This value will have been set by a previous
call to (putprop a1 s1 a2). Example:
-->(get 'frank 'lastname)
(getd a1)
~~~~~~~~~
Will return the lambda, nlambda or macro expression that is
associated with a1 or nil if no such expression is associated
with a1.
(getenv a1)
~~~~~~~~~~~
Will return an atom whose print name is the string set by
environment variable a1. For example we can get the PATH variable
setting by entering (getenv 'PATH). Note that these must be in
upper case for MSDOS.
(hashtabstat)
~~~~~~~~~~~~~
Will return a list containing 250 float atoms. Each float
atoms value represents the number of elements in the bucket for
that hash location in the atom hash table. 250 is the size of the
hash table. This is not especially useful for you but it gives me
a way of checking how the hashing function is distributing the
atoms. Large bucket values will not effect any execution times
except those of explode, implode, ascii and gensym. In other
words only functions that create new atoms will be slowed down by
poor hashing. Reading will always suffer from large hash bucket
values.
(memstat) { not present in Franz }
~~~~~~~~~
returns three floats. The first is the percentage of cell
space that is in use. The second is the percentage of alpha cell
space and the third is the percentage of heap space in use. When
any of these reach 100%, garbage collection will occur. Alpha and
cell space is collected together. Heap space is only collected
when you run out. After garbage collection you will see these
three percentages drop. The alpha and cell percentages should
drop to tell you how much memory is actually in use at that
moment. The heap space when compacted and gathered will not
necessarily drop to indicate how much you really have left. This
is because heap space is gathered in blocks of 16K, not all at
once as with atoms and cells. So, there will almost certainly be
more than 20% free heap space in other non compacted blocks even
if memstat reports 80% of the heap space is in use.
15
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CONT'D
~~~~~~
(oblist)
~~~~~~~~
Returns a list of every known object in the system at the
current moment. Note that if you call oblist and assign the
result somewhere you will cause every one of those objects to be
kept by the system. If there are lots of large alpha atoms the
heap and alpha space will be tied up until you set the assigned
variable to some other value.
(plist a1)
~~~~~~~~~~
Will return the property list for atom a1. The property list
is of the form ((ke1 . value1)(key2 . value2)...(keyn . valuen)).
Note that plist returns a top level copy of the property list
because remprop destroys this lists top level structure.
(putd a1 l1)
~~~~~~~~~~~~
Identical to "def" except that the parameters a1 and l1 are
evaluated. This allows you to write functions that create
functions and add them to the LISP interpreter.
(putprop a1 s1 a2)
~~~~~~~~~~~~~~~~~~
Adds to the property list of a1 the value s1 associated with
the property indicator a2. It returns the value of a1. For
example: (putprop 'Peter 'AshwoodSmith 'lastname)
(remprop a1 a2)
~~~~~~~~~~~~~~~
Removes the property associated with key a2 from the
property list of atom a1. The top level structure of the property
list is actually destroyed. It returns the old property list
starting at the point where the deletion was made.
(set a1 s1)
~~~~~~~~~~~
Will bind a1 to s1 at current scope level or globally if no
scope exists for a1 yet. Set returns s1.
(setplist a1 l1)
~~~~~~~~~~~~~~~~
Will set the property list of atom a1 to the list l1 where
the list must be ((keyn.valn)..). It returns this new list l1.
(setq *a1* s1 *a2* s2 ..... *an* sn)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows an infinite number of variable and value pairs and it
does not evaluate the variables a1...an. So (setq a 'val1 b
'val2) binds val1 to a and val2 to b. Setq will return sn.
16
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CONT'D
~~~~~~
(trace [*a1* *a2* *a3* ..... *an*])
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Will turn on tracing of the user defined functions a1...an.
Note that you cannot trace built in functions. If you call trace
with no parameters it will return a list of all user defined
functions that have been set for tracing by a previous call to
trace, otherwise trace returns exactly the list (a1 a2...an)
after enabling tracing of each of these user defined functions.
If any of the atoms is not a user defined function trace stops
and returns an error. All atoms up to the point of error will be
traced.
(untrace [*a1* *a2* *a3* ..... *an*])
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Will disable tracing of the listed functions which must all
be user defined. If no parameters are given it disables tracing
of all functions. Untrace returns a list of all functions whose
tracing has been disabled. Here is a demonstration of how you can
use them. The --> is the LISP prompt. This is the sort of
sequence that you should see on the console. The comments ;...
were added to tell you what is going on.
-->(defun factorial(n) ; define n! = n * (n-1)!
(cond ((= n 0) 1)
(t (* n (factorial (- n 1))))))
factorial
-->(trace factorial) ; ask LISP to trace n!
(factorial)
-->(factorial 5) ; ask LISP for 5!
<enter> factorial( 5 ) ; entered with parm=5
<enter> factorial( 4 ) ; " " " 4
<enter> factorial( 3 ) ; " " " 3
<enter> factorial( 2 ) ; " " " 2
<enter> factorial( 1 ) ; " " " 1
<enter> factorial( 0 ) ; " " " 0
<EXIT> factorial 1 ; exit 0! = 1
<EXIT> factorial 1 ; exit 1! = 1
<EXIT> factorial 2 ; exit 2! = 1
<EXIT> factorial 6 ; exit 3! = 6
<EXIT> factorial 24 ; exit 4! = 24
<EXIT> factorial 120 ; exit 5! = 120
120
-->(untrace factorial) ; ask LISP to shut up
(factorial)
-->(factorial 5) ; now it is quiet again.
120
-->
17
FUNCTIONS WITH SIDE EFFECTS OR THAT ARE EFFECTED BY SYSTEM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CONT'D
~~~~~~
(showstack)
~~~~~~~~~~~
This function will display a copy of the last 20 eval
and apply evaluations from the internal stack. The top of the
internal stack is copied whenever LISP is about to enter the
break level (prompt 'er>'). This means that if you execute some
function and it aborts prematurely you can call showstack from
the break level and see exactly what lead to the error. Whenever
a new error occurs the old copy of the top 20 elements on the
internal stack is lost and a new trace is copied for you to
display via (showstack). This is unlike Franz which allows
infinite break levels. For example consider this example session
with PC-LISP which is similar to an example in LISPcraft.
-->(defun foobar(y)(prog(x)(setq x (cons (car 8) y]
foobar
-->(foobar '(a b c))
--- error evaluating built in function [car] ---
er>x
()
er>y
(a b c)
er>(showstack)
[] (car 8)
[] (car 8)
[] (cons <**> y)
[] (setq x <**>)
[] (prog(x) <**>)
[] (foobar '(a b c))
t
In this example I declared a function called 'foobar' which
runs a prog and does a single assignment to x. When I execute it
with parameter '(a b c). PC-LISP correctly tells me that there
was an error evaluating the built in function 'car'. I can
examine the values of x and y and see that x is still set to the
empty list () that the prog call set it to. y is bound to the
parameter passed to foobar as expected. Next I called (showstack)
to see the trace of execution. I see that the top evaluation (car
8) is the culprit. The evaluation previous to that is also (car
8) but this evaluation was before the arguments had been
evaluated. Remember that floats eval to themselves. The <**>
symbols in the show stack are just a short hand way of saying
look at the entry above to see what the <**> should be replaced
with. This greatly reduces the amount of information that you
have to look at when you read a stack dump. It also allows you to
follow the stream of partial evaluations by looking at each <**>
in turn. Note that infinite recursion leaves a stream of <**>'s.
18
LIST EVALUATION CONTROL FUNCTIONS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These functions are the control flow functions for LISP they
effect which lists are evaluated and how. They operate on the
basic LISP function type which is a lambda expression. Labeled
lambda expressions are also allowed.
(lambda l1 s1....sn)
~~~~~~~~~~~~~~~~~~~~
This is not a function but it is a list construct which
can act as a function in any context where a function is legal. A
lambda expression is a function body. The S-expressions s1..sn
are expressions that are evaluated in the order s1...sn. The
result is the evaluation of sn. The atoms in the list l1 are
called bound variables. They will be bound to values that occur
on the right of the lambda expression before the S-expressions
s1..sn are evaluated and unbound after the value of sn is
returned.
(nlambda l1 s1....sn)
~~~~~~~~~~~~~~~~~~~~~
This is a function body construct similar to lambda but with
a few major differences. The first is that the list l1 must only
specify one formal parameter. This will be set to a list of the
UNEVALUATED parameters that fall on the right of the nlambda
expression when it is being evaluated. This function allows you
to write functions with a variable number of parameters and to
control the evaluation of these parameters. For example we can
write a function called 'plus that behaves the same way as '+ in
all contexts as follows:
-->(def plus (nlambda(l)(eval(cons '+ l))))
or
-->(defun plus fexpr(l)(eval(cons '+ l)))
Both of which create the same nlambda expression. This
function will behave as follows when spotted on the left of a
sequence of parameters 1 2 3 4. First it will not evaluate the
sequence of parameters 1 2 3 4. Second it makes these into a list
(1 2 3 4). It then binds 'l to this list and evaluates the
expression (eval(cons( '+ l))). This expression results in (eval
(+ 1 2 3 4)). Which is just the desired result 10.
(label a1 (lambda|nlambda l1 s1..sn))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This acts just like a lambda expression except that the body
is temporarily bound to the name a1 for evaluation of the body
s1. This allows recursive calls to the same body. The binding of
the body to the name a1 will be forgotten as soon as the
expression s1 terminates the recursion. For example:
(label LastElement (lambda(List)
(cond ((null (cdr List))(car List))
(t (LastElement (cdr List))))))
19
LIST EVALUATION CONTROL FUNCTIONS CONT'D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(apply s1 l1)
~~~~~~~~~~~~~
The function s1 is evaluated in the context resulting from
binding its formal parameters to the values in l1. The result of
this evaluation is returned. Example:
-->(apply '(lambda(x y z)(* (+ x y) z)) '(2 3 4))
20
(cond l1 l2 ... ln)
~~~~~~~~~~~~~~~~~~~
The lists l1 ... ln are checked on by one. They are of the
form (s1 s2). Cond evaluates the s1's one by one until it finds
one that does not eval to nil. It then returns the result of
evaluating the corresponding s2. If all of the s1's (called
guards) evaluate to nil, it returns 'nil. For example:
-->(cond ((eq (type t) 'float) '|t is a float|)
((eq (type t) 'list) '|t is a list |)
(t '|t not list or float|)))
|t not list or float|
(eval s1)
~~~~~~~~~
Runs the LISP interpreter on the S-expression s1. It just
removes a quote from the expression s1. For example:
-->(eval '(+ 2 4))
6
(mapcar s1 l1 l2 l3 .... ln)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function will map the function s1 onto the parameter
list made by taking the car of each of l1...ln. It forms a list
of the results of the repeated application of s1 to the next
elements in the lists l1...ln. It stops when the list l1 runs out
of elements. Note that each of l1...ln should have the same
number of elements, although this condition is not checked for
and nil will be substituted if a list runs out of elements before
the others. Extra elements in any list are ignored. For example:
-->(mapcar '< '(10 20 30) '(11 19 30))
(t nil nil)
Which returns the results of (< 10 11) (< 20 19) and (< 30
30) as the list (t nil nil). Note that s1 could be any built in
function, user defined function or lambda expression. For
example:
-->(mapcar 'putprop '(John Fred Bill)
'(Mary Sue Linda)
'(mother sister daughter))
(Mary Sue Linda)
20
LIST EVALUATION CONTROL FUNCTIONS CONT'D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(defun a1 macro l1 s1 s2 ... sn)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Macro is a special body, similar to nlambda except that it
causes code replacement when it is evaluated. An example is the
best explanation I can give you: (Read LISPcraft example)
-->(defun first-elemet macro(l)(cons 'car (cdr l)))
first-element
-->(setq x '(first-element '(a b c)))
(first-element '(a b c))
-->(eval x)
a
-->x
(car '(a b c))
-->(eval x)
a
In the example above I have first declared a macro called
'first-element' which when run given a list parameter should
return the first element in the list. I could have done this
using a lambda expression but this would require parameter
binding etc every time I execute 'first-element'. Rather, what I
have chosen to do is to cause (first-element x) to be replaced by
the code (car x) everywhere it is encountered. Then future
execution of (first-element x) is just as costly as an execution
of (car x). This is accomplished as follows: When a macro is
encountered, eval passes the entire expression (first-element
(quote a b c)) to the macro body. This body is (cons 'car (cdr
l)) and is evaluated in the context where the entire expression
is bound to the macro parameter l. This results in the code
fragment (car (quote a b c)) which is substituted in the code for
the original (first-element (quote (a b c))) expression and
evaluated giving 'a. The above example demonstrates this by
showing what happens to the value of a variable 'x before and
after evaluation of the macro. Note the change in the value of x
but that the result of (eval x) remains the same. That is the
whole purpose of macros.
PC-LISP macros have two limitations that Franz macros do not
have. A PC-LISP macro MUST return a piece of code that is a list.
YOU CANNOT RETURN AN ATOM FROM A MACRO. Secondly a PC-LISP macro
must have been def'ined, defun'ed, or putd'ed, otherwise it
will not function correctly. Ie YOU CANNOT USE IT LIKE A LAMBDA
OR NLAMBDA BODY WITHOUT A NAME.
(macroexpand s1)
~~~~~~~~~~~~~~~~
This function lets you see at what the macro expansion of s1
looks like prior to evaluation and substitution. For example:
-->(macroexpand '(first-element '(a b c)))
(car '(a b c))
21
LIST EVALUATION CONTROL FUNCTIONS CONT'D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(prog l1 s1.....sn)
~~~~~~~~~~~~~~~~~~~
Prog is a way of escaping the pure LISP applicative
programming environment. It allows you to evaluate a sequence of
S-expressions one after the other in true imperative style. It
allows you to use the functions (go..) and (return ..) to perform
the goto and return functions that imperative languages permit.
Prog operates as follows: The list l1 which is a list of atom
names is scanned and each atom is bound to nil at this scope
level. Next the S-expressions s1..sn are scanned once. If any of
s1..sn are atoms they are bound to the S-expression that follows
them. Next we start evaluating lists s1...sn ignoring the atoms
which are assumed to be labels. If we ever evaluate a S-
expression and it has the form (return Z) we unbind all the atoms
in l1 and all labels, and return the result Z. If we ever
encounter a list of the form (go loc) we reset our next list to
be the location given by loc. If at any time we reach sn, and it
is not a go or a return, we simply unbind all of l1 and the
labels in s1...sn and return the result of evaluating sn.
Entering and exiting progs is a little expensive time wise, it is
probably better not to nest them if you are interested in
performance. Note prog labels must be alpha or literal alpha
atoms:
For example:
-->(prog (List SumOfAtoms)
(setq List (hashtabstat))
(setq SumOfAtoms 0)
LOOP (cond ((null List) (return SumOfAtoms)))
(setq SumOfAtoms (+ (car List) SumOfAtoms))
(setq List (cdr List))
(go LOOP)
)
306
This peice of code operates as follows. First it creates two
local variables. Next it binds the variable List to the list of
hash bucket totals from the alpha hash table. It then sets a sum
counter to 0. Next it checks the List variable to see if it is
nil. If so it returns the Sum Of all the Atoms. Otherwise it adds
the first float in the list List to the running SumOfAtoms, winds
in the list List by one, and jumps to LOOP. Note also that we can
accomplish the same thing as the above prog with the much simpler
examples which follow:
-->(eval (cons '+ (hashtabstat)))
306
-->(length(oblist))
306
22
MSDOS BIOS CALLS FOR GRAPHICS OUTPUT
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These functions are still experimental. They do however
allow you to play with drawing recursive curves etc. They
all result in an INT 10H. This means that the graphics
should be portable to any MSDOS machine and should run under any
windowing environment like topview or mswindows. This is why they
are so slow. Note that they all return 't. They do not check to
see if the INT call was successful or not.
(#scrline# f1 f2 f3 f4 f5)
~~~~~~~~~~~~~~~~~~~~~~~~~~
Draws a line on the screen connecting (f1,f2) with the point
(f3,f4) using attribute f5. This function calls the BIOS set dot
function for each point. Hence it is not very fast.
(#scrmde# f1)
~~~~~~~~~~~~~
Sets the video mode to f1. Modes are values between 0 and 9.
(8 and 9) are high resolution for the Tandy2000 and I suppose are
high resolution modes on other machines that support the (640 x
400) or greater graphics resolutions. These are all listed in
your hardware reference manual but basically they are: 0 =
40x25B&W, 1=40x25COL, 2=80x25B&W 3=80x25COL, 4 =320x200COL,
5=320x200B&W, 6=640x200B&W, 7=reserved, 8=640x400COL,
9=640x400B&W. This is as of DOS 2.11.
(#scrsap# f1)
~~~~~~~~~~~~~
Sets the active video page to f1. f1 should be between 0 and
8. This is valid for text modes only. Versions of MSDOS other
than 2.11 may not support this call.
(#scrscp# f1 f2 f3)
~~~~~~~~~~~~~~~~~~~
Sets the cursor position to be in page f1 at row f2 and in
column f3. Where 0 is the top row and 0 is leftmost col.
(#scrsct# f1 f2)
~~~~~~~~~~~~~~~~
Sets the cursor type to agree with the following: f1 bit
5 (0 = blink 1 = steady), bit 6 (0 = visible, 1 = invisible),
bits 4-0 = start line for cursor within character cell. f2 bits
4-0 = end line for cursor within character cell.
(#scrwdot# f1 f2 f3)
~~~~~~~~~~~~~~~~~~~~
Write a dot (pixel). The pixel at row f1 and column f2 has
its color value XORed with the color attribute f3. Since the
color attributes vary from machine to machine you will have to
look up the correct values for this parameter.
23
TECHNICAL INFORMATION
~~~~~~~~~~~~~~~~~~~~~
The interpreter is written using the Lattice C compiler
version 2.03. It consists of 7 separate modules. A scanner,
parser, memory manager, list evaluator and critical functions
module, a built in functions module, a library of extra Unix libc
functions not provided by Lattice C consisting of assembly
language routines for setjmp(), longjmp() and getenv(), and
finally a modified C start up assembly language module to provide
signal trapping for stack overflow and control-break.
Memory is organized as follows. Alpha cells have fields for
a shallow stack of bindings, a pointer to heap space for the
print names, a pointer to any built in or user defined functions,
and a pointer to any property lists. Alpha cells are the largest
of all the cells and have their own fixed storage area. Heap
space which is just the space used for the print names of the
alpha cells may be variable sized blocks of up to 254 bytes long.
These will be extended in future versions of PC-LISP to allow
allocation of up to 64K blocks for use by arrays etc. The rest of
the cells used by PC-LISP are all considered as one. This
consists of the float, cons, and port cells. They have their own
contiguous slice of memory. This means that three different
contiguous types of memory are required. It is managed in the
following way. At start up time the percentages of memory are
read from the default settings or the environment variables
LISP%HEAP and LISP%ALPH. Next memory is allocated in 16K chunks
these are the largest contiguous pieces handled by the
memory manager. These are all kept track of in a large vector of
pointers. Next groups of these blocks are primed for use by
alpha,cell, or heap managers. These managers handle the
distribution and reclamation of memory in their block. The heap
manager will perform compaction and relocation to get free space.
The alpha and cell managers will perform mark and gather garbage
collection to get space. The heap manager may request mark and
gather collection if there is a real shortage of heap space.
Stack overflow detection is done by intercepting the call to
the Lattice C stack overflow routine, temporarily resetting the
stack, and them making a call to my own C stack overflow routine.
This then longjmps out of the error condition. The Unix version
handles the error in the same way except that the overflow
results in a SIGSEGV which then calls the same routine.
Control-BREAK detection is done via periodic testing of the
status in the evaluator main loop, and the read main loop. When a
break is detected control is transferred to the break handler
which prints a message and longjmps back to the mainline code.
The Unix version will have made a signal call asking that the
break handler be executed when a user break key is hit. Hence the
results are the same. CONTROL-C checking is done in the same way
except that a CONTROL-C will only be spotted on I/O so a looping
non printing function can only be stopped with CONTROL-BREAK.
Note that CONTROL-BREAK is INT 1BH and CONTROL-C is INT 23H.
24
KNOWN BUGS OR LACKING FEATURES OF V2.07
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-It is possible to run out of stack space while garbage
collecting. This occasionally happens with machines that have
only 256K of RAM. I have never seen it happen with > 256K of RAM
however it can probably still happen. This is because the garbage
collector requires stack space to operate. A better version of
the garbage collection that uses no stack space will be
introduced in a later version. If the stack does overflow during
garbage collection, an appropriate error message is printed. A
link inverting marking traversal is slated for the next version
of PC-LISP and will fix this problem.
-You cannot input floats in exponential notation. This is
because the LISP lexical analyzer does not yet recognize them.
-Line drawing is not too quick, or too clean. The lines take
time to draw because they go through the BIOS, they are not very
clean at certain slopes due to some bugs. But the video graphics
routines are still experimental so do not rely on them too much.
You will also note that several other video INT calls are
missing.
-If too many (load 'file) calls fail you will run out of
available ports. This is because they are left open. PC-LISP does
not close open load ports if an error occurs while reading from
them.
-Two special atoms with rather obscure names should never be
printed from within a prog. These are $[|return|]$ and $[|go|]$.
If you attempt to print these from within a prog, the print
function will return them and this will confuse the heck out of
prog which uses them for internal purposes. This can occur if you
try to use a prog to print the contents of the oblist. If you
must print the oblist atom by atom to a file or the screen, make
sure you 'absorb' the result of any print or patom calls.
-You are not prevented from altering the bindings of t and
nil. This means that if you use t or nil as the name of a
parameter somewhere it will get temporarily bound to something
else while that procedure is executing. You can also set or setq
them to other values. Attempts to alter their bindings should
obviously be trapped but are not in V2.07.
-Macros must return a peice of code which is a list. Atoms
cannot be returned. Franz allows either but to alter PC-LISP
would require some medium scale surgery that I do not want to
undertake unless the feature is really missed.
25
KNOWN BUGS OR LACKING FEATURES OF V2.07 (CONT'D)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The interpreter is pretty slow. This is caused by the
continuous error checking, and the use of an extra stack for
marking intermediate results. Speed-ups are possible but make the
program less robust so I am hesitant to add them. The fact that
there is no integer type also causes slow-downs when you are
using a float as a loop counter. Floats however are more useful
in general than ints so of the two I decided to omit integers for
the first large scale release. It is interesting to note that
some benchmarks that I ran on a not too busy Vax 11/750 were only
about 2 times as fast (waiting time) as my Tandy 2000 (80186 @
8Mhz). This is not really a fair comparison though because Franz
is a much bigger and more complex program. I would be interested
to know how PC-LISP performs on other MS-DOS machines.
RE BUGS OR DESIRED ENHANCMENTS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I have tried to think of everything that a user could do to
crash the system and protect him/her from it but I'm sure my
imagination has only covered half of the possibilities. If you
find any other bugs or if you think some features would be nice
to add to PC-LISP, I will consider them for the next major
release (which will probably not be until September 86). Please
don't hesitate to let me know what you think, good or bad. I'd
appreciate the feed back as I have put a lot of work into this
program and want to know what you people out there think of it.
Regards
Peter Ashwood-Smith.
26